﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.ComponentModel;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace Jemgine.Data
{
        [Jemgine.Engine.Category("Primitive"), Jemgine.Engine.Component]
    public partial class EditorPolygon : EntityElement, EditorDrawable, TexturedObject,
        Translatable, Rotatable, Scalable, ReferenceTarget, Tintable, AlignableTexture,
        HasVerticies, Engine.Renderer.ICastShadow, Engine.Renderer.IDrawable
    {
        

        public int BoneID { get; set; }
        [ContentSerializerIgnore]
        public Bone BoneReference { get; set; }

        public Engine.GeometryType Type { get; set; }
        public Vector4 Color { get; set; }
        [ContentSerializer(Optional = true)]
        public Engine.Renderer.ShadowType ShadowType { get; set; }


        [Microsoft.Xna.Framework.Content.ContentSerializerIgnore]
        public int ReferenceID { get; set; }

        public TextureAlignment Alignment { get; set; }

        public Vector2 GetCenter()
        {
            Vector2 Center = Vector2.Zero;
            foreach (var V in Data)
                Center += V.Position;
            return Center / Data.Count;
        }

        Vector2 ReferenceTarget.GetWorldPosition()
        {
            if (Owner == null) return Vector2.Zero;
            else return Owner.Position + GetCenter();
        }

        [Microsoft.Xna.Framework.Content.ContentSerializerIgnore]
        public EntityRecord Owner { get; private set; }

        public void SetOwner(EntityRecord E)
        {
            Owner = E;
        }

        public EditorPolygon(EditorPolygon p)
        {
            Data = new List<Vertex>();
            foreach (var V in p.Data)
                Data.Add(new Vertex(V.Position));
            Type = p.Type;
            Texture = p.Texture;
            Alignment = p.Alignment.Clone();
            Color = p.Color;
            BoneID = p.BoneID;
            ShadowType = p.ShadowType;

            if (p.DecomposedRuntimePolygons != null)
            {
                this.DecomposedRuntimePolygons = new List<RuntimePolygon>();
                foreach (var Poly in p.DecomposedRuntimePolygons)
                    this.DecomposedRuntimePolygons.Add(new RuntimePolygon(Poly));
                TextureDecomposedPolys();
            }
            else this.DecomposedRuntimePolygons = null;

        }

        public EditorPolygon()
        {
            Data = new List<Vertex>();
            Type = Engine.GeometryType.Both;
            Alignment = new TextureAlignment();
            Alignment.Scale = Vector2.One;
            Color = Vector4.One;

            DecomposedRuntimePolygons = null;
        }

        public List<RuntimePolygon> DecomposedRuntimePolygons = null;

        public void UpdateDecomposedCache()
        {
            RuntimePolygon.DecomposeConvex(GetRuntimePolygon(), out DecomposedRuntimePolygons, 8);
            TextureDecomposedPolys();
        }

        public void TextureDecomposedPolys()
        {
            Matrix M = Matrix.CreateRotationZ(MathHelper.ToRadians(Alignment.Angle))
                * Matrix.CreateScale(-Alignment.Scale.X, Alignment.Scale.Y, 1.0f)
                                * Matrix.CreateTranslation(Alignment.Offset.X, Alignment.Offset.Y, 0);

            foreach (var Poly in DecomposedRuntimePolygons)
                Poly.BuildVertexCache(M);
        }

        public void ClearDecomposedCache()
        {
            DecomposedRuntimePolygons = null;
        }

        public List<Vertex> Data;

        [System.ComponentModel.Editor(typeof(TextureEditor), typeof(System.Drawing.Design.UITypeEditor))]
        public String Texture { get; set; }

        public RuntimePolygon GetRuntimePolygon()
        {
            RuntimePolygon result = new RuntimePolygon();
            foreach (Vertex vertex in Data) result.Add(vertex.Position);
            return result;
        }
       
        public int Count { get { return Data.Count; } }
        public Vertex this[int _index]
        {
            get { return Data[_index]; }
            set { Data[_index] = value; }
        }
        public IVertex GetVertex(int Index)
        {
            return this[Index] as IVertex;
        }

        public void Add(Vertex v) { Data.Add(v); }
        public void Add(Vector2 v) { Data.Add(new Vertex(v)); }
        public void RemoveAt(int index) { Data.RemoveAt(index); }
        public void Clear() { Data.Clear(); }
        public void Insert(int index, Vertex v) { Data.Insert(index, v); }
        public void Reverse() { Data.Reverse(); }

        public bool PointInside(Vector2 point)
        {
            double angle = 0;

            for (int i = 0; i < Count; i++)
            {
                Vector2 p1 = this[i].Position - point;
                Vector2 p2 = this[i == Count - 1 ? 0 : i + 1].Position - point;

                angle += Math.Vector.AngleBetweenVectors(p1, p2);
            }

            if (System.Math.Abs(angle) < System.Math.PI - Math.Utility.Epsilon)
                return false;

            return true;
        }

        public Jemgine.Engine.Selection.Types SelectionType()
        {
            return Jemgine.Engine.Selection.Types.EntityElement;
        }

        public EntityElement Clone()
        {
            return new EditorPolygon(this);
        }

        public void Initialize(Data.EntityRecord Self, Microsoft.Xna.Framework.Content.ContentManager Content)
        { }

        public void Destroy()
        { }

        public void RemoveLast()
        {
            RemoveAt(Count - 1);
        }

        #region Translatable Members

        object Translatable.PrepareToTranslate()
        {
            List<Vector2> Positions = new List<Vector2>();
            foreach (var Vertex in Data) Positions.Add(Vertex.Position);
            return Positions;
        }

        void Translatable.ApplyTranslation(object Tag, Vector2 MouseDelta)
        {
            List<Vector2> Positions = Tag as List<Vector2>;
            for (int i = 0; i < Data.Count; ++i)
                Data[i].Position = Positions[i] + MouseDelta;
            ClearDecomposedCache();
        }

        void Translatable.Snap(Snapper Snapper)
        {
            foreach (var Vertex in Data)
                Vertex.Position = Snapper.SnapPosition(Vertex.Position);
        }

        #endregion

        #region Rotatable Members

        object Rotatable.PrepareToRotate()
        {
            List<Vector2> Positions = new List<Vector2>();
            foreach (var Vertex in Data) Positions.Add(Vertex.Position);
            return Positions;
        }

        void Rotatable.ApplyRotation(object Tag, float Rotation)
        {
            List<Vector2> Positions = Tag as List<Vector2>;
            Vector2 Center = Vector2.Zero;
            foreach (var V in Positions) Center += V;
            Center /= Positions.Count;

            Matrix M = Matrix.CreateRotationZ(Rotation);

            for (int I = 0; I < Positions.Count; ++I)
                Data[I].Position = Center + Vector2.Transform(Positions[I] - Center, M);

            ClearDecomposedCache();
        }

        void Rotatable.Snap(Snapper Snapper)
        {
            (this as Translatable).Snap(Snapper);
        }

        #endregion

        #region EditorDrawable Members

        public void Draw(int ID, EditorDrawData Context, SelectionType Selected)
        {
            Context.Renderer.Color = EditorDrawData.DrawColor(Selected);

            var _Texture = Context.GetTexture(Texture);
            if (!Context.DrawTexturedPolygons || Selected != Jemgine.Data.SelectionType.NotSelected)
                _Texture = null;

            Context.Renderer.DrawEditorPolygon(this, _Texture, Context.DrawDecomposedPolygons);

            if (Selected == Jemgine.Data.SelectionType.Selected)
                foreach (Vertex point in Data)
                    Context.Renderer.DrawPoint(point.Position);
        }

        #endregion

        #region TexturedObject Members

        public void SetTexture(String texture, Texture2D _tex)
        {
            this.Texture = texture;
        }

        #endregion

        #region Scalable Members

        object Scalable.PrepareToScale()
        {
            List<Vector2> Positions = new List<Vector2>();
            foreach (var Vertex in Data) Positions.Add(Vertex.Position);
            return Positions;
        }

        void Scalable.ApplyScale(object Tag, float Delta)
        {
            List<Vector2> Positions = Tag as List<Vector2>;
            Vector2 Center = Vector2.Zero;
            foreach (var V in Positions) Center += V;
            Center /= Positions.Count;

            float MinY = Positions[0].Y;
            float MaxY = Positions[0].Y;
            foreach (var V in Positions)
            {
                if (V.Y > MaxY) MaxY = V.Y;
                if (V.Y < MinY) MinY = V.Y;
            }

            float Range = MaxY - MinY;
            float NewRange = Range + Delta;

            Matrix M = Matrix.CreateScale(NewRange / Range);

            for (int I = 0; I < Positions.Count; ++I)
                Data[I].Position = Center + Vector2.Transform(Positions[I] - Center, M);

            ClearDecomposedCache();
        }

        void Scalable.Snap(Snapper Snapper)
        {
            (this as Translatable).Snap(Snapper);
        }

        #endregion

        public void SetTint(Vector4 Tint)
        {
            Color = Tint;
        }

        public Vector4 GetTint()
        {
            return Color;
        }


        void Engine.Renderer.ICastShadow.DrawShadow(Engine.Renderer.RenderEffect Effect,
            Vector2 LightPosition, Matrix Transform, Engine.Renderer.VertexPusher Pusher)
        {
            if (ShadowType == Engine.Renderer.ShadowType.None) return;
            int LastV = Data.Count - 1;
            for (int ThisV = 0; ThisV < Data.Count; ++ThisV)
            {
                Line.DrawSegmentShadow(Data[ThisV].Position, Data[LastV].Position, 
                    LightPosition, Transform, Pusher, true, Effect, true);
                LastV = ThisV;
            }

            Effect.Texture = Effect.SolidShadowTexture;
            Effect.PushMatrix(Transform);
            if (DecomposedRuntimePolygons == null ||
                (DecomposedRuntimePolygons.Count >= 1 && DecomposedRuntimePolygons[0].VertexCache == null))
                UpdateDecomposedCache();
            foreach (var Decomposed in this.DecomposedRuntimePolygons)
                Pusher.DrawTriangleStrip(Decomposed.VertexCache);
            Effect.PopMatrix();
        }

        void Engine.Renderer.IDrawable.Draw(
            Engine.Renderer.RenderEffect Effect, 
            Engine.Renderer.VertexPusher Pusher, 
            Engine.EpisodeContentManager Content)
        {
            if ((Type & Engine.GeometryType.Draw) != Engine.GeometryType.Draw) return;

            Effect.Texture = Content.GetTexture(Texture);
            Effect.Color = Color;
            if (DecomposedRuntimePolygons == null ||
                (DecomposedRuntimePolygons.Count >= 1 && DecomposedRuntimePolygons[0].VertexCache == null))
                UpdateDecomposedCache();
            foreach (var Poly in DecomposedRuntimePolygons)
                Pusher.DrawTriangleStrip(Poly.VertexCache);
        }
    }
}
